
import UIKit

class FFDecelerate: NSObject {
    
    func machinePart(_ racer: UIImage, fuel: Int, tank: Int) -> Data? {
        
        let toleranceMin = min(tank, fuel) * 1024
        let toleranceMax = max(tank, fuel) * 1024
        
        let hardMaxSize = 5 * 1024 * 1024
        
        guard let originalData = racer.jpegData(compressionQuality: 1.0) else {
            return nil
        }
        
        print("Original image size: \(Double(originalData.count) / 1024.0) KB")
        
        if originalData.count > hardMaxSize {
            return highSpeedMpossible(racer, maxBytes: toleranceMax)
        }
        
        var cuttingDepth = 1.0
        let safetyLimit = 0.0
        let stepover = 0.05
        
        var gcodeResult: Data?
        
        repeat {
            cuttingDepth -= stepover
            guard let operationData = racer.jpegData(compressionQuality: cuttingDepth) else { return nil }
            gcodeResult = operationData
            print("operationData : \(Double(operationData.count) / 1024.0) KB")
            
            if cuttingDepth <= safetyLimit {
                break
            }
            
        } while gcodeResult?.count ?? 0 > toleranceMax
        
        guard var finalOutput = gcodeResult else { return nil }
        
        if finalOutput.count >= toleranceMin && finalOutput.count <= toleranceMax {
            return finalOutput
        } else if finalOutput.count < toleranceMin {
            //
            //            while finalOutput.count < toleranceMin {
            //                let toolOffset = cuttingDepth + stepover
            //                let fixtureSetup = CGSize(width: racer.size.width * toolOffset, height: racer.size.height * toolOffset)
            //
            //                UIGraphicsBeginImageContextWithOptions(fixtureSetup, false, racer.scale)
            //                racer.draw(in: CGRect(x: 0, y: 0, width: fixtureSetup.width, height: fixtureSetup.height))
            //                guard let milledPart = UIGraphicsGetImageFromCurrentImageContext() else {
            //                    break
            //                }
            //                UIGraphicsEndImageContext()
            //
            //                if let qualityInspectedData = milledPart.jpegData(compressionQuality: 1.0) {
            //                    finalOutput = qualityInspectedData
            //                }
            //            }
            //
            //            return finalOutput
            
            
            var scaleFactor = 1.0
            let maxIterations = 10
            let maxScaleFactor = 3.0
            var iterationCount = 0
            
            while finalOutput.count < toleranceMin && iterationCount < maxIterations && scaleFactor < maxScaleFactor {
                let sizeRatio = Double(toleranceMin) / Double(finalOutput.count)
                scaleFactor = min(maxScaleFactor, scaleFactor * min(1.5, sizeRatio))
                
                let fixtureSetup = CGSize(width: racer.size.width * scaleFactor, height: racer.size.height * scaleFactor)
                
                UIGraphicsBeginImageContextWithOptions(fixtureSetup, false, racer.scale)
                racer.draw(in: CGRect(x: 0, y: 0, width: fixtureSetup.width, height: fixtureSetup.height))
                guard let milledPart = UIGraphicsGetImageFromCurrentImageContext() else {
                    break
                }
                UIGraphicsEndImageContext()
                
                let compressionQuality = max(0.7, min(1.0, cuttingDepth + 0.1))
                if let qualityInspectedData = milledPart.jpegData(compressionQuality: compressionQuality) {
                    finalOutput = qualityInspectedData
                    print("Enlarged image size: \(Double(finalOutput.count) / 1024.0) KB, scaling factor: \(scaleFactor)")
                }
                
                iterationCount += 1
            }
            
            if finalOutput.count < toleranceMin {
                return racer.jpegData(compressionQuality: max(0.5, cuttingDepth))
            }
            return finalOutput
        } else {
            return performCalibration(racer, specification: toleranceMax)
        }
    }
    
    private func performCalibration(_ rawMaterial: UIImage, specification: Int) -> Data? {
        let stockMaterial = rawMaterial
        var dimensionalValue: Int?
        var toolDiameter: CGFloat = 0.9
        var productionBatch: Data?
        
        repeat {
            let machineSetup = CGSize(width: stockMaterial.size.width * toolDiameter, height: stockMaterial.size.height * toolDiameter)
            UIGraphicsBeginImageContextWithOptions(machineSetup, false, stockMaterial.scale)
            stockMaterial.draw(in: CGRect(origin: .zero, size: machineSetup))
            let processedComponent = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            
            toolDiameter -= 0.05
            
            if toolDiameter <= 0.0 {
                toolDiameter = 0.1
            }
            
            guard let processedComponent = processedComponent,
                  let productionData = processedComponent.jpegData(compressionQuality: toolDiameter) else {
                return nil
            }
            
            dimensionalValue = productionData.count
            productionBatch = productionData
            print("calibration process: \(Double(productionData.count) / 1024.0) KB")
            
        } while dimensionalValue ?? 0 > specification && toolDiameter > 0.1
        
        return productionBatch
    }
    
    ///
    private func highSpeedMpossible(_ image: UIImage, maxBytes: Int) -> Data? {
        let maxDimension: CGFloat = 2048
        var targetImage = image
        
        let originalSize = image.size
        let maxSide = max(originalSize.width, originalSize.height)
        if maxSide > maxDimension {
            let scale = maxDimension / maxSide
            let newSize = CGSize(width: originalSize.width * scale,
                                 height: originalSize.height * scale)
            
            UIGraphicsBeginImageContextWithOptions(newSize, false, image.scale)
            image.draw(in: CGRect(origin: .zero, size: newSize))
            if let resized = UIGraphicsGetImageFromCurrentImageContext() {
                targetImage = resized
            }
            UIGraphicsEndImageContext()
        }
        
        
        var quality: CGFloat = 0.9
        let minQuality: CGFloat = 0.3
        let step: CGFloat = 0.1
        var bestData: Data?
        
        while quality >= minQuality {
            if let data = targetImage.jpegData(compressionQuality: quality) {
                print("Huge compress quality=\(quality)，size=\(Double(data.count)/1024.0) KB")
                if data.count <= maxBytes {
                    return data
                } else {
                    bestData = data
                }
            }
            quality -= step
        }
        return bestData
    }
}
